/***************************************************************************
 *   Copyright (C) 2008 by Justin L. Boss                                  *
 *   justin@rtechguys.com                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "seacreature.h"
#include <QtDebug>
#include <QFile>

SeaCreature::SeaCreature (const char filename[])
{
    RX = 0;
    RY = 0;
    RZ = 0;
    TailPathCount=qrand()%56;
    Size = 1;
    TailSpan = 1;
    Mammal = false;

    CurMtx = new GLfloat[16];
    OldMtx = new GLfloat[16];
    MtxRtX   = new GLfloat[16];
    MtxRtY   = new GLfloat[16];
    MtxRtZ   = new GLfloat[16];
    MtxTrn   = new GLfloat[16];

    ifstream aglfile (filename);

    aglfile >> BoneSize;

    BonMtx = new GLfloat *[BoneSize];
    for (int i=0; i<BoneSize; i++)
        BonMtx[i] = new GLfloat[16];

    Bones = new GLfloat[BoneSize];
    HalfBone = BoneSize / 2;
    for (int i=0;i<BoneSize-1;i++)
        aglfile >> Bones[i];

    aglfile >> MeshSize;

    TransMesh = new MeshData[MeshSize];

    StaticMesh = new SMeshData[MeshSize];

    for (int i=0;i<MeshSize;i++)
    {
        aglfile >> StaticMesh[i].vx;
        aglfile >> StaticMesh[i].vy;
        aglfile >> StaticMesh[i].vz;
        aglfile >> StaticMesh[i].nx;
        aglfile >> StaticMesh[i].ny;
        aglfile >> StaticMesh[i].nz;
        aglfile >> StaticMesh[i].bv;
        aglfile >> StaticMesh[i].bi;
    }

    aglfile >> UVCordSize;
    UVCord = new UVData[UVCordSize];
    for (int i=0;i<UVCordSize;i++)
    {
        aglfile >> UVCord[i].x;
        aglfile >> UVCord[i].y;
    }

    aglfile >> FaceSize;
    Faces = new FaceData[FaceSize];
    for (int i=0;i<FaceSize;i++)
    {
        aglfile >> Faces[i].a;
        aglfile >> Faces[i].b;
        aglfile >> Faces[i].c;
        aglfile >> Faces[i].d;
    }
    aglfile.close();

    UpdateTail();
    Time.start();
    LastTime = Time.elapsed();
}

SeaCreature::~SeaCreature()
{
    delete CurMtx;
    delete OldMtx;
    delete MtxRtX;
    delete MtxRtY;
    delete MtxRtZ;
    delete MtxTrn;

    delete Bones;

    for (int i=0; i<BoneSize; i++)
        delete [] BonMtx[i];
    delete [] BonMtx;

    delete TransMesh;

    delete StaticMesh;

    delete UVCord;

    delete Faces;
}

void SeaCreature::SetTailSpan(int span)
{
    TailSpan = span * 0.1;
}

void SeaCreature::SetSize(int size)
{
    Size = size * 0.1;
}

void SeaCreature::LoadIdentity()
{
    memcpy(CurMtx, Identity, 64);
    memcpy(MtxRtX, Identity, 64);
    memcpy(MtxRtY, Identity, 64);
    memcpy(MtxRtZ, Identity, 64);
    memcpy(MtxTrn, Identity, 64);
}


void SeaCreature::CatMtx (GLfloat *newmtx)
{
    OldMtx[0]  = CurMtx[0] * newmtx[0]  + CurMtx[4] * newmtx[1]  + CurMtx[8]  * newmtx[2]  + CurMtx[12] * newmtx[3];
    OldMtx[1]  = CurMtx[1] * newmtx[0]  + CurMtx[5] * newmtx[1]  + CurMtx[9]  * newmtx[2]  + CurMtx[13] * newmtx[3];
    OldMtx[2]  = CurMtx[2] * newmtx[0]  + CurMtx[6] * newmtx[1]  + CurMtx[10] * newmtx[2]  + CurMtx[14] * newmtx[3];
    OldMtx[3]  = CurMtx[3] * newmtx[0]  + CurMtx[7] * newmtx[1]  + CurMtx[11] * newmtx[2]  + CurMtx[15] * newmtx[3];
    OldMtx[4]  = CurMtx[0] * newmtx[4]  + CurMtx[4] * newmtx[5]  + CurMtx[8]  * newmtx[6]  + CurMtx[12] * newmtx[7];
    OldMtx[5]  = CurMtx[1] * newmtx[4]  + CurMtx[5] * newmtx[5]  + CurMtx[9]  * newmtx[6]  + CurMtx[13] * newmtx[7];
    OldMtx[6]  = CurMtx[2] * newmtx[4]  + CurMtx[6] * newmtx[5]  + CurMtx[10] * newmtx[6]  + CurMtx[14] * newmtx[7];
    OldMtx[7]  = CurMtx[3] * newmtx[4]  + CurMtx[7] * newmtx[5]  + CurMtx[11] * newmtx[6]  + CurMtx[15] * newmtx[7];
    OldMtx[8]  = CurMtx[0] * newmtx[8]  + CurMtx[4] * newmtx[9]  + CurMtx[8]  * newmtx[10] + CurMtx[12] * newmtx[11];
    OldMtx[9]  = CurMtx[1] * newmtx[8]  + CurMtx[5] * newmtx[9]  + CurMtx[9]  * newmtx[10] + CurMtx[13] * newmtx[11];
    OldMtx[10] = CurMtx[2] * newmtx[8]  + CurMtx[6] * newmtx[9]  + CurMtx[10] * newmtx[10] + CurMtx[14] * newmtx[11];
    OldMtx[11] = CurMtx[3] * newmtx[8]  + CurMtx[7] * newmtx[9]  + CurMtx[11] * newmtx[10] + CurMtx[15] * newmtx[11];
    OldMtx[12] = CurMtx[0] * newmtx[12] + CurMtx[4] * newmtx[13] + CurMtx[8]  * newmtx[14] + CurMtx[12] * newmtx[15];
    OldMtx[13] = CurMtx[1] * newmtx[12] + CurMtx[5] * newmtx[13] + CurMtx[9]  * newmtx[14] + CurMtx[13] * newmtx[15];
    OldMtx[14] = CurMtx[2] * newmtx[12] + CurMtx[6] * newmtx[13] + CurMtx[10] * newmtx[14] + CurMtx[14] * newmtx[15];
    OldMtx[15] = CurMtx[3] * newmtx[12] + CurMtx[7] * newmtx[13] + CurMtx[11] * newmtx[14] + CurMtx[15] * newmtx[15];
    GLfloat * temp = OldMtx;
    OldMtx = CurMtx;
    CurMtx = temp;
}


void SeaCreature::Draw (GLfloat offset, int speed, GLfloat height)
{
    glBindTexture (GL_TEXTURE_2D, TextureName);

    gsBegin (((Time.elapsed() - LastTime) % 5000) * speed * 0.05);
    glPushMatrix();

    if (RX)
        glRotatef (RX, 1.0, 0.0, 0.0);
    if (RY)
        glRotatef (RY, 0.0, 1.0, 0.0);
    if (RZ)
        glRotatef (RZ, 0.0, 0.0, 1.0);

    if (Mammal)
        glRotatef (TailPath[int(TailPathCount)%56][0]*0.7, 0.0, 0.0, 1.0);

    glBegin (GL_QUADS);
    for (int x=0; x<FaceSize; x++)
    {
        glTexCoord2f (UVCord[Faces[x].a].x, UVCord[Faces[x].a].y);
        glNormal3f (TransMesh[Faces[x].a].nx, TransMesh[Faces[x].a].ny, TransMesh[Faces[x].a].nz);
        glVertex3f (TransMesh[Faces[x].a].vx-offset, TransMesh[Faces[x].a].vy + height, TransMesh[Faces[x].a].vz);

        glTexCoord2f (UVCord[Faces[x].b].x, UVCord[Faces[x].b].y);
        glNormal3f (TransMesh[Faces[x].b].nx, TransMesh[Faces[x].b].ny, TransMesh[Faces[x].b].nz);
        glVertex3f (TransMesh[Faces[x].b].vx-offset, TransMesh[Faces[x].b].vy + height, TransMesh[Faces[x].b].vz);

        glTexCoord2f (UVCord[Faces[x].c].x, UVCord[Faces[x].c].y);
        glNormal3f (TransMesh[Faces[x].c].nx,TransMesh[Faces[x].c].ny,TransMesh[Faces[x].c].nz);
        glVertex3f (TransMesh[Faces[x].c].vx-offset,TransMesh[Faces[x].c].vy + height, TransMesh[Faces[x].c].vz);

        glTexCoord2f (UVCord[Faces[x].d].x, UVCord[Faces[x].d].y);
        glNormal3f (TransMesh[Faces[x].d].nx,TransMesh[Faces[x].d].ny,TransMesh[Faces[x].d].nz);
        glVertex3f (TransMesh[Faces[x].d].vx-offset,TransMesh[Faces[x].d].vy + height, TransMesh[Faces[x].d].vz);
    }
    glEnd();
    glPopMatrix();
    gsEnd();

    TailPathCount += (Time.elapsed() - LastTime) * speed * 0.001;
    LastTime = Time.elapsed();

    UpdateTail();
}

void SeaCreature::Draw (GLfloat offset, int speed, GLfloat gsMatrix[])
{
    glBindTexture (GL_TEXTURE_2D, TextureName);

    gsBegin (((Time.elapsed() - LastTime) % 5000) * speed * 0.05);

    glPushMatrix();

    glLoadMatrixf (gsMatrix);
    if (RX)
        glRotatef (RX, 1.0, 0.0, 0.0);
    if (RY)
        glRotatef (RY, 0.0, 1.0, 0.0);
    if (RZ)
        glRotatef (RZ, 0.0, 0.0, 1.0);

    if (Mammal)
        glRotatef (TailPath[int(TailPathCount)%56][0]*0.7, 0.0, 0.0, 1.0);

    glBegin (GL_QUADS);
    for (int x=0; x<FaceSize; x++)
    {
        glTexCoord2f (UVCord[Faces[x].a].x, UVCord[Faces[x].a].y);
        glNormal3f (TransMesh[Faces[x].a].nx, TransMesh[Faces[x].a].ny, TransMesh[Faces[x].a].nz);
        glVertex3f (TransMesh[Faces[x].a].vx-offset, TransMesh[Faces[x].a].vy, TransMesh[Faces[x].a].vz);

        glTexCoord2f (UVCord[Faces[x].b].x, UVCord[Faces[x].b].y);
        glNormal3f (TransMesh[Faces[x].b].nx, TransMesh[Faces[x].b].ny, TransMesh[Faces[x].b].nz);
        glVertex3f (TransMesh[Faces[x].b].vx-offset, TransMesh[Faces[x].b].vy, TransMesh[Faces[x].b].vz);

        glTexCoord2f (UVCord[Faces[x].c].x, UVCord[Faces[x].c].y);
        glNormal3f (TransMesh[Faces[x].c].nx,TransMesh[Faces[x].c].ny,TransMesh[Faces[x].c].nz);
        glVertex3f (TransMesh[Faces[x].c].vx-offset,TransMesh[Faces[x].c].vy,TransMesh[Faces[x].c].vz);

        glTexCoord2f (UVCord[Faces[x].d].x, UVCord[Faces[x].d].y);
        glNormal3f (TransMesh[Faces[x].d].nx,TransMesh[Faces[x].d].ny,TransMesh[Faces[x].d].nz);
        glVertex3f (TransMesh[Faces[x].d].vx-offset,TransMesh[Faces[x].d].vy,TransMesh[Faces[x].d].vz);
    }
    glEnd();
    glPopMatrix();//
    gsEnd();

    TailPathCount += (Time.elapsed() - LastTime) * speed * 0.001;
    LastTime = Time.elapsed();

    UpdateTail();
}

void SeaCreature::Rotate (GLfloat X, GLfloat Y, GLfloat Z)
{
    if (X)
    {
        MtxRtX[5]= cos(X*PI_180);
        MtxRtX[9]= sin(X*PI_180);
        MtxRtX[6]= -MtxRtX[9];
        MtxRtX[10]= MtxRtX[5];
        CatMtx (MtxRtX);
    }
    if (Y)
    {
        MtxRtY[0]= cos(Y*PI_180);
        MtxRtY[2]= sin(Y*PI_180);
        MtxRtY[8]= -MtxRtY[2];
        MtxRtY[10]= MtxRtY[0];
        CatMtx (MtxRtY);
    }
    if (Z)
    {
        MtxRtZ[0]= cos (Z*PI_180);
        MtxRtZ[4]= sin (Z*PI_180);
        MtxRtZ[1]=-MtxRtZ[4];
        MtxRtZ[5]= MtxRtZ[0];
        CatMtx (MtxRtZ);
    }
}

void SeaCreature::Translate (GLfloat TX, GLfloat TY, GLfloat TZ)
{
    MtxTrn[12]=TX;
    MtxTrn[13]=TY;
    MtxTrn[14]=TZ;
    CatMtx (MtxTrn);
}

void SeaCreature::Scale (GLfloat SX, GLfloat SY, GLfloat SZ)
{
    CurMtx[0] =SX;
    CurMtx[5] =SY;
    CurMtx[10]=SZ;
}

void SeaCreature::SetTexture (GLuint name)
{
    TextureName = name;
}

void SeaCreature::UpdateTail()						// New faster one.
{
    LoadIdentity();
    Scale (Size, Size, Size);
    CpyMtx (BonMtx[0]);
    for (int i = 1; i < BoneSize; i++)
    {
        Translate (Bones[i-1], 0.0 ,0.0);
        if (Mammal)
            Rotate (0, 0, TailPath[int(TailPathCount)%56][int((i-1)/HalfBone)]*TailSpan);
        else
            Rotate (0, TailPath[int(TailPathCount)%56][int((i-1)/HalfBone)]*TailSpan, 0);
        CpyMtx (BonMtx[i]);
    }
    for (int x=0; x < MeshSize; x++)
    {
        TransMesh[x].vx = StaticMesh[x].vx * BonMtx[StaticMesh[x].bi][0] + StaticMesh[x].vy * BonMtx[StaticMesh[x].bi][4] + StaticMesh[x].vz * BonMtx[StaticMesh[x].bi] [8] + BonMtx[StaticMesh[x].bi][12];
        TransMesh[x].vy = StaticMesh[x].vx * BonMtx[StaticMesh[x].bi][1] + StaticMesh[x].vy * BonMtx[StaticMesh[x].bi][5] + StaticMesh[x].vz * BonMtx[StaticMesh[x].bi] [9] + BonMtx[StaticMesh[x].bi][13];
        TransMesh[x].vz = StaticMesh[x].vx * BonMtx[StaticMesh[x].bi][2] + StaticMesh[x].vy * BonMtx[StaticMesh[x].bi][6] + StaticMesh[x].vz * BonMtx[StaticMesh[x].bi][10] + BonMtx[StaticMesh[x].bi][14];
        TransMesh[x].nx = StaticMesh[x].nx * BonMtx[StaticMesh[x].bi][0] + StaticMesh[x].ny * BonMtx[StaticMesh[x].bi][4] + StaticMesh[x].nz * BonMtx[StaticMesh[x].bi] [8];
        TransMesh[x].ny = StaticMesh[x].nx * BonMtx[StaticMesh[x].bi][1] + StaticMesh[x].ny * BonMtx[StaticMesh[x].bi][5] + StaticMesh[x].nz * BonMtx[StaticMesh[x].bi] [9];
        TransMesh[x].nz = StaticMesh[x].nx * BonMtx[StaticMesh[x].bi][2] + StaticMesh[x].ny * BonMtx[StaticMesh[x].bi][6] + StaticMesh[x].nz * BonMtx[StaticMesh[x].bi][10];
    }
}

void SeaCreature::CpyMtx(GLfloat *destmatrix)
{
    destmatrix [0]=CurMtx [0]; destmatrix [1]=CurMtx [1]; destmatrix [2]=CurMtx [2]; destmatrix [3]=CurMtx [3];
    destmatrix [4]=CurMtx [4]; destmatrix [5]=CurMtx [5]; destmatrix [6]=CurMtx [6]; destmatrix [7]=CurMtx [7];
    destmatrix [8]=CurMtx [8]; destmatrix [9]=CurMtx [9]; destmatrix[10]=CurMtx[10]; destmatrix[11]=CurMtx[11];
    destmatrix[12]=CurMtx[12]; destmatrix[13]=CurMtx[13]; destmatrix[14]=CurMtx[14]; destmatrix[15]=CurMtx[15];
}

void SeaCreature::scRotate(GLfloat X, GLfloat Y, GLfloat Z)
{
    RX = X;
    RY = Y;
    RZ = Z;
}
